# Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
# MemCache_Hybrid is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#          http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.

cmake_minimum_required(VERSION 3.12.0)
project(memcache VERSION 1.0 LANGUAGES CXX C)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF)

if (BUILD_COMPILER MATCHES gcc)
    #    set(CMAKE_COMPILER gcc)
else ()
    set(CMAKE_COMPILER bisheng)
endif()

if (CMAKE_COMPILER MATCHES bisheng AND DEFINED ENV{ASCEND_HOME_PATH})
    set(ASCEND_HOME_PATH $ENV{ASCEND_HOME_PATH})
    set(CMAKE_C_COMPILER ${CMAKE_COMPILER})
    set(CMAKE_CXX_COMPILER ${CMAKE_COMPILER})
else ()
    if (CMAKE_COMPILER MATCHES bisheng)
        message(WARNING "ASCEND_HOME_PATH not found! Please run set_env.sh first.")
    endif()
    #    set(CMAKE_C_COMPILER gcc)
    #    set(CMAKE_CXX_COMPILER g++)
endif ()

option(ENABLE_TRACE_LOG "Enable trace log" OFF)
option(ENABLE_ASAN "Enable ASAN" OFF)
option(BUILD_TESTS "build test or not" OFF)
option(BUILD_OPEN_ABI "build open _GLIBCXX_USE_CXX11_ABI" ON)
option(ENABLE_CPU_MONOTONIC "enable monotonic time using cpu hard instruction" OFF)
option(BUILD_GIT_COMMIT "build library version with commit" ON)
option(BUILD_GIT_COMMIT_GEN_FILE "generate commit log file" ON)
option(BUILD_PYTHON "build python file" ON)
option(ENABLE_PTRACER "build ptracer" OFF)
option(USE_CANN "BM implemented using CANN." ON)
option(BUILD_CPP_EXAMPLE "Compile and package the c++ example." OFF)

if (NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE RELEASE)
endif ()

message(STATUS "BUILD_TESTS = ${BUILD_TESTS}")
message(STATUS "BUILD_USE_CXX11_ABI = ${BUILD_OPEN_ABI}")
message(STATUS "ENABLE_CPU_MONOTONIC = ${ENABLE_CPU_MONOTONIC}")
message(STATUS "BUILD_GIT_COMMIT = ${BUILD_GIT_COMMIT}")
message(STATUS "BUILD_GIT_COMMIT_GEN_FILE = ${BUILD_GIT_COMMIT_GEN_FILE}")
message(STATUS "BUILD_PYTHON = ${BUILD_PYTHON}")
message(STATUS "ENABLE_PTRACER = ${ENABLE_PTRACER}")
message(STATUS "CMAKE_COMPILER = ${CMAKE_COMPILER}")
message(STATUS "CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}")

add_compile_options(-Wunused-variable -Wunused-value -Wcast-align)
add_compile_options(-fpermissive -Wno-error)
add_compile_options(-Wcast-qual -Winvalid-pch -Wwrite-strings -Wsign-compare -Wfloat-equal -Wextra -Wno-deprecated-declarations)

if (${CMAKE_BUILD_TYPE} MATCHES "RELEASE")
    #add_compile_options(-fvisibility=hidden -fvisibility-inlines-hidden)
    add_compile_options(-fstack-protector-strong)
    add_compile_options(-Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -fPIE -fPIC -ftrapv -s)
    add_compile_options(-D_FORTIFY_SOURCE=2 -O2)
    add_link_options(-Wl,-z,noexecstack,-z,relro,-z,now -pie)
    add_link_options(-s)
    set(CMAKE_SKIP_RPATH TRUE)
    message(STATUS "build type set to RELEASE")
else ()
    add_compile_options(-g -ggdb3 -O0 -fno-omit-frame-pointer -rdynamic -fPIC)
endif ()

if (${CMAKE_BUILD_TYPE} MATCHES "ASAN")
    # 开启多种 sanitizer   "address,undefined,pointer-compare,pointer-subtract,alignment"
    set(SANITIZERS "address,undefined,pointer-compare,pointer-subtract,alignment")
    # 编译选项
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=${SANITIZERS}")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=${SANITIZERS}")
    # 链接选项
    add_link_options(-fsanitize=${SANITIZERS})
endif ()

if (BUILD_TESTS STREQUAL "ON")
    add_definitions(-DUT_ENABLED)
    if (NOT CMAKE_COMPILER MATCHES bisheng)
        add_compile_options(-fprofile-arcs -ftest-coverage)
        add_link_options(-lgcov --coverage)
    endif()
    set(BUILD_OPEN_ABI "ON")
endif ()


if (ENABLE_PTRACER STREQUAL "ON")
    add_definitions(-DENABLE_PTRACER=ON)
endif ()

if (USE_CANN STREQUAL "ON")
    add_definitions(-DUSE_CANN=ON)
    message("USE_CANN IS ON.")
endif ()

if (NOT BUILD_OPEN_ABI STREQUAL "ON")
    add_compile_options(-D_GLIBCXX_USE_CXX11_ABI=0)
endif ()

if (ENABLE_CPU_MONOTONIC STREQUAL "ON")
    add_compile_options(-DENABLE_CPU_MONOTONIC)
endif ()

# set log commit into compile definition
if (BUILD_GIT_COMMIT STREQUAL "ON")
    find_program(GIT_EXECUTABLE NAMES git)
    if (EXISTS ${GIT_EXECUTABLE})
        # get commit id from file
        execute_process(
            COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
            RESULT_VARIABLE GIT_COMMIT_RESULT
            OUTPUT_VARIABLE GIT_LAST_COMMIT
            OUTPUT_STRIP_TRAILING_WHITESPACE)
        if (${GIT_COMMIT_RESULT} EQUAL 1)
            set(GIT_LAST_COMMIT "empty")
        endif ()
        add_definitions(-DGIT_LAST_COMMIT=${GIT_LAST_COMMIT})
        message(STATUS "add definition for last commit: ${GIT_LAST_COMMIT}")
    else ()
        add_definitions(-DGIT_LAST_COMMIT=empty)
        message(STATUS "Failed to find git command, not GIT_LAST_COMMIT will be set")
    endif ()
else ()
    add_definitions(-DGIT_LAST_COMMIT=empty)
    message(STATUS "add definition for last commit: empty")
endif ()

if (ENABLE_TRACE_L0G)
    add_definitions(-DLOG_TRACE_INFO_ENABLED)
endif ()

set(PROJECT_BUILD_PATH ${PROJECT_BINARY_DIR})
set(PROJECT_3RDPARTY_SRC_DIR ${PROJECT_SOURCE_DIR}/3rdparty)
set(PROJECT_3RDPARTY_BIN_DIR ${PROJECT_SOURCE_DIR}/output/3rdparty)
set(PROJECT_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/output)

# set src base dir and output dir of memcache
set(PROJECT_MMC_SRC_BASE ${PROJECT_SOURCE_DIR}/src/memcache)
set(PROJECT_MMC_OUTPUT ${PROJECT_SOURCE_DIR}/output/memcache)

# set install target top dir
message(STATUS "CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}")
if (${CMAKE_INSTALL_PREFIX} MATCHES "/usr/local")
    set(TARGET_INSTALL_DIR ${PROJECT_OUTPUT_PATH}/)
elseif (NOT EXISTS ${CMAKE_INSTALL_PREFIX})
    set(TARGET_INSTALL_DIR ${PROJECT_OUTPUT_PATH}/)
else ()
    set(TARGET_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/)
endif ()
message(STATUS "install dir ${TARGET_INSTALL_DIR}")

if( (BUILD_PYTHON STREQUAL "ON") OR (BUILD_TESTS STREQUAL "ON") )
    # 获取Python头文件路径
    find_program(PYTHON_EXECUTABLE NAMES python3 python)
    if(NOT PYTHON_EXECUTABLE)
        message(FATAL_ERROR "Python not found")
    endif()

    execute_process(
            COMMAND ${PYTHON_EXECUTABLE} -c
            "import sysconfig; print(sysconfig.get_path('include'))"
            OUTPUT_VARIABLE PYTHON_INCLUDE_DIR
            OUTPUT_STRIP_TRAILING_WHITESPACE
    )

    execute_process(
            COMMAND ${PYTHON_EXECUTABLE} -c
            "import sys; import os; import pybind11; print(os.path.dirname(pybind11.__file__))"
            OUTPUT_VARIABLE PYTHON_PYBIND11_HOME
            OUTPUT_STRIP_TRAILING_WHITESPACE
    )

    set(pybind11_cmake_dir "${PYTHON_PYBIND11_HOME}/share/cmake/pybind11")

    # 检查路径是否存在且是目录
    if(NOT EXISTS "${pybind11_cmake_dir}" OR NOT IS_DIRECTORY "${pybind11_cmake_dir}")
        message(SEND_ERROR "Can not find pybind11 directory: ${pybind11_cmake_dir}\nPlease 'pip3 install pybind11 or pybind11-dev'")
    endif()
endif()

# 判断环境变量 CMAKE_PREFIX_PATH 是否存在
if(DEFINED ENV{CMAKE_PREFIX_PATH})
    # 如果环境变量存在，将其值赋给 CMake 变量
    set(CMAKE_PREFIX_PATH "$ENV{CMAKE_PREFIX_PATH}")
    list(APPEND CMAKE_PREFIX_PATH "${pybind11_cmake_dir}")
    message(STATUS "CMAKE_PREFIX_PATH is exist, and append: ${pybind11_cmake_dir}")
    message(STATUS "Now CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}")
else()
    # 如果环境变量不存在，设置 CMake 变量的默认值
    set(CMAKE_PREFIX_PATH "${pybind11_cmake_dir}")
    message(STATUS "Not set CMAKE_PREFIX_PATH, and initialize to: ${CMAKE_PREFIX_PATH}")
endif()

include_directories(
        ${PROJECT_SMEM_SRC_BASE}/include/host
        ${PROJECT_UTIL_SRC_BASE}
)

add_subdirectory(3rdparty)

set(HYBRID_SRC ${PROJECT_SOURCE_DIR}/3rdparty/memfabric_hybrid/src)
file(GLOB_RECURSE ALL_HEADERS
        "${HYBRID_SRC}/*.h"
)
set(INCLUDE_DIRS "")
foreach (header ${ALL_HEADERS})
    get_filename_component(HEADER_DIR ${header} PATH)
    list(APPEND INCLUDE_DIRS ${HEADER_DIR})
endforeach ()
list(REMOVE_DUPLICATES INCLUDE_DIRS)

include_directories(${INCLUDE_DIRS})

add_subdirectory(src)

if (BUILD_CPP_EXAMPLE)
    add_subdirectory(example/cpp)
endif ()

if(BUILD_TESTS STREQUAL "ON")
    set(CMAKE_CXX_STANDARD 17)
    message(STATUS "BUILD_TESTS = ON, add compile gov")
    add_subdirectory(test)
endif()